use crate::{
    database_error::DatabaseError, model::User, schema::users::dsl::*, until::try_connect,
};
use apperror::{AnyError, AnyResult, ErrKind, Overlay};
use diesel::{
    prelude::*,
    r2d2::{ConnectionManager, PooledConnection},
};
//根据用户名称和密码查找
impl User {
    pub fn update_the_user_info(&mut self, user: User) {
        self.set_username(user.get_username().to_owned());
        self.set_password(user.get_password().to_owned());
    }
    fn get(
        &self,
        conn: &mut PooledConnection<ConnectionManager<MysqlConnection>>,
    ) -> Result<User, diesel::result::Error> {
        let result = users
            .filter(username.eq(self.get_username()))
            .select(User::as_select())
            .first::<User>(conn)?;
        println!("{:#?}", result);
        Ok(result)
    }
    #[inline]
    fn exist(
        &self,
        conn: &mut PooledConnection<ConnectionManager<MysqlConnection>>,
    ) -> anyhow::Result<bool> {
        let result = diesel::select(diesel::dsl::exists(
            users.filter(username.eq(self.get_username())),
        ))
        .get_result::<bool>(conn)?;
        let _ = match result {
            true => Ok(()),
            false => Err(DatabaseError::UserIsExist(self.get_username().to_owned())),
        }?;
        Ok(true)
    }
    pub fn is_exist(&self) -> anyhow::Result<bool> {
        let mut conn = try_connect()?;
        Ok(self.exist(&mut conn)?)
    }
    pub fn get_instance_from_database(&self) -> AnyResult<User> {
        let mut conn: PooledConnection<ConnectionManager<MysqlConnection>> = try_connect().unwrap();
        let result = self.get(&mut conn).map_err(|_| {
            AnyError::builder()
                .message("the username not found")
                .kind(ErrKind::EntityAbsence)
                .context("username", self.get_username())
                .build()
        })?;
        Ok(result)
    }
    pub fn delete_instance_from_database(&self) -> AnyResult<()> {
        let mut conn =
            try_connect().map_err(|e| AnyError::wrap(e).overlay("获取数据库连接时出现错误!"))?;
        conn.transaction::<_, diesel::result::Error, _>(|conn| {
            let _ = self.get(conn)?;
            let target = users.filter(username.eq(self.get_username()));
            diesel::delete(target).execute(conn)?;
            Ok(())
        })
        .map_err(|e| AnyError::wrap(e))?;

        Ok(())
    }
    pub fn insert_instance_to_database(self) -> AnyResult<usize> {
        let mut conn =
            try_connect().map_err(|e| AnyError::wrap(e).overlay("获取数据库连接出现错误!"))?;
        let count = conn
            .transaction::<_, anyhow::Error, _>(|conn| {
                self.exist(conn)?;
                let insert_user = diesel::insert_into(users).values(&self).execute(conn)?;
                Ok(insert_user)
            })
            .map_err(|e| AnyError::builder().context("Error", e).build())?;
        println!("{:#?}", count);
        Ok(count)
    }
}
#[deprecated(note = "please use struct method")]
pub fn insert_user(user: User) -> AnyResult<usize> {
    let mut conn =
        try_connect().map_err(|e| AnyError::wrap(e).overlay("获取数据库连接出现错误!"))?;
    let count = conn
        .transaction::<_, anyhow::Error, _>(|conn| {
            user.exist(conn)?;
            let insert_user = diesel::insert_into(users).values(&user).execute(conn)?;
            Ok(insert_user)
        })
        .map_err(|e| AnyError::builder().context("Error", e).build())?;
    println!("{:#?}", count);
    Ok(count)
}

//测试
#[cfg(test)]
mod test_select {
    use crate::until::{build_pool, DB_POOL};
    use dotenv::dotenv;
    use std::env;

    #[test]
    fn test_select() {
        dotenv().ok();
        let database_url = env::var("DATABASE_URL").expect("database_url must be set");
        DB_POOL
            .set(
                build_pool(&database_url, 10)
                    .expect(&format!("Error connecting to {}", &database_url)),
            )
            .ok();
    }
}
#[cfg(test)]
#[allow(deprecated)]
mod test_insert {
    use std::env;

    use dotenv::dotenv;

    use crate::{
        model::User,
        until::{build_pool, DB_POOL},
    };

    use super::insert_user;

    #[test]
    fn insert() {
        dotenv().ok();
        let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
        DB_POOL
            .set(build_pool(&database_url, 10).expect("error connect to mysql"))
            .ok();
        let mut news_user = User::default();
        news_user.set_username("yzj".to_string());
        news_user.set_password("123456".to_string());
        let value = insert_user(news_user).unwrap();
        println!("{}", value);
    }
    #[test]
    fn exist() {
        dotenv().ok();
        let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
        DB_POOL
            .set(build_pool(&database_url, 10).expect("error connect to mysql"))
            .ok();
        let mut news_user = User::new()
            .username("root".to_string())
            .password("123456".to_string());
        println!("{:#?}", news_user.is_exist());
    }
}
